/*****************************************************
** Author: 
** Date: 2020-05-06
*
*****************************************************/

#include "dna_os.h"
#include "dna_errno.h"
#include "dna_common.h"

#define pd_log(_fmt_, ...) \
        printf("[PD][%s:%d][%s] "_fmt_"\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__)
#define pd_log_err(_fmt_, ...) \
        printf("[PD][ERR][%s:%d][%s] "_fmt_"\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__)

#define BL_PRIVATE_DATE_POOL_MAX    4

enum {
    BL_PD_STATE_NEW,
    BL_PD_STATE_REQUESTING,
};

/* private date operations */
struct pd_ops {
    struct pd_ops *next;
    unsigned char state;
    unsigned int reqtype;
    unsigned int rsptype;
    int (*makup_msg_handler)(unsigned char *output, int maxsize);
    int (*parse_msg_handler)(unsigned char *input, int ilen);
    unsigned long timeout_ticks;
    unsigned int timeout_cnt;
};

static struct pd_ops pd_pools[BL_PRIVATE_DATE_POOL_MAX];
static struct pd_ops *pd_free = NULL;
static struct pd_ops *pd_used = NULL;

// unit: s
// const static unsigned char pd_timeout[] = {3, 3, 3, 6, 9, 12, 15, 30};
const static unsigned char pd_timeout[] = {3, 6, 9};

/**
 * @brief
 * @param  None
 * @retval None
 */
static void dump_pd_link(struct pd_ops *list, const char *str)
{
    struct pd_ops *p;
    int i;
    pd_log("pd_ops dump %s", str);
    for (p = list, i = 0; p != NULL; p = p->next, i++) {
        pd_log("[%d] p %p", i, p);
    }
}

int bl_pd_init(void)
{
    struct pd_ops *head = pd_pools;
    int i;

    for (i = 0; i < (BL_PRIVATE_DATE_POOL_MAX - 1); ++i)
        head[i].next = &head[i+1];

    head[i].next = NULL;

    pd_free = head;
    pd_used = NULL;

    dump_pd_link(pd_free, "bl_pd_init free");
    dump_pd_link(pd_used, "bl_pd_init used");
    return DNA_SUCCESS;
}

int bl_pd_request(unsigned int reqtype, unsigned int rsptype, \
                int (*makup)(unsigned char *, int), \
                int (*parse)(unsigned char *, int))
{
    if (pd_free == NULL) {
        pd_log_err("pd pool is full");
        return -DNA_FAIL;
    }

    struct pd_ops *new = pd_free;
    pd_free = new->next;

    new->state = BL_PD_STATE_NEW;
    new->reqtype = reqtype;
    new->rsptype = rsptype;
    new->makup_msg_handler = makup;
    new->parse_msg_handler = parse;
    new->timeout_ticks = 0;
    new->timeout_cnt = 0;

#if 0
    /* put the new node at the head of using list */
    new->next = pd_used;
    pd_used = new;
#else
    /* put the new node at the tail of using list */
    struct pd_ops **pwalk = &pd_used;
    pd_log("pwalk %p *pwalk %p", pwalk, *pwalk);
    while ((*pwalk) != NULL) {
        pd_log("(*pwalk)->next %p &((*pwalk)->next) %p", (*pwalk)->next, &((*pwalk)->next));
        pwalk = &((*pwalk)->next);
        pd_log("pwalk %p *pwalk %p", pwalk, *pwalk);
    }

    (*pwalk) = new;
    new->next = NULL;
#endif

    dump_pd_link(pd_free, "bl_pd_request free");
    dump_pd_link(pd_used, "bl_pd_request used");
    return DNA_SUCCESS;
}

int bl_pd_handler(unsigned char *input, int ilen)
{
    struct pd_ops **pwalk = &pd_used;

    pd_log("pwalk %p *pwalk %p", pwalk, *pwalk);
    while ((*pwalk) != NULL && (*pwalk)->rsptype != 10023) {
        pwalk = &((*pwalk)->next);
        pd_log("pwalk %p *pwalk %p", pwalk, *pwalk);
    }
    if ((*pwalk) == NULL)
        return DNA_SUCCESS;

    pd_log("*pwalk %p (*pwalk)->next %p", *pwalk, (*pwalk)->next);

    int ret = (*((*pwalk)->parse_msg_handler))(input, ilen);
    pd_log("parse msg ret %d", ret);

    /* delete this node */
    struct pd_ops *pdel = *pwalk;
    *pwalk = (*pwalk)->next;

    pdel->next = pd_free;
    pd_free = pdel;

    dump_pd_link(pd_free, "bl_pd_handler free");
    dump_pd_link(pd_used, "bl_pd_handler used");
    return DNA_SUCCESS;
}

static int bl_pd_do_request(struct pd_ops *node)
{
    unsigned char buff[256];

    // TODO: make up head

    int olen = (*(node->makup_msg_handler))(buff, sizeof(buff));
    if (olen < 0) {
        pd_log_err("make up msg failed %d", olen);
        return -DNA_FAIL;
    }
    pd_log("msg %d %.*s", olen, olen, buff);

    // TODO: send msg

    pd_log("req timeout %ds", pd_timeout[node->timeout_cnt]);
    node->timeout_ticks = dna_system_ticks() + pd_timeout[node->timeout_cnt++] * 1000;
    return DNA_SUCCESS;
}

void bl_pd_polling(void)
{
    struct pd_ops **pwalk = &pd_used;
    unsigned char drop = FALSE;
    char *errstr;

    if ((*pwalk) == NULL) 
        return ;

    switch ((*pwalk)->state) {
        case BL_PD_STATE_NEW:
        if (bl_pd_do_request(*pwalk) < 0) {
            drop = TRUE;
            errstr = "make up msg failed";
        } else {
            (*pwalk)->state = BL_PD_STATE_REQUESTING;
        }
        break;

        case BL_PD_STATE_REQUESTING:
        if (dna_system_ticks() > (*pwalk)->timeout_ticks) {
            if ((*pwalk)->timeout_cnt >= sizeof(pd_timeout)) {
                drop = TRUE;
                errstr = "maximum re-try";
            } else if (bl_pd_do_request(*pwalk) < 0) {
                drop = TRUE;
                errstr = "make up msg failed";
            }
        }
        break;

        default:
        break;
    }

    if (drop) {
        struct pd_ops *pdel = *pwalk;
        *pwalk = (*pwalk)->next;

        pdel->next = pd_free;
        pd_free = pdel;
        pd_log_err("drop msg reason:%s", errstr);

        dump_pd_link(pd_free, "bl_pd_polling free");
        dump_pd_link(pd_used, "bl_pd_polling used");
    }
}

/* End of file */
